﻿using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using System.Linq;
using ProjectBordspil.Models;
using System.Web.Security;
using System.Collections.Generic;

namespace ProjectBordspil.Controllers
{
    [Authorize]
    public class GuiGameHub : Hub
    {
        AppDataContext context = new AppDataContext();

        List<string> connectedUsers = new List<string>();

        /// <summary>
        /// Adds a connected user to a group. Players playing the same instance of a particular game
        /// belong to the same group.
        /// </summary>
        public void Join(string groupId)
        {
            Groups.Add(Context.ConnectionId, groupId);
        }

        /// <summary>
        /// The function initialises the users that connect to the game as user1, user2 and currentPlayer
        /// </summary>
        public void InitialiseUsers(string groupName)
        {
            int nGameId = 0; 
            string user1;

            if (Int32.TryParse(groupName, out nGameId))
            {
                user1 = GetUserOne(nGameId);

                Clients.Group(groupName).showUserOne(user1);

                Clients.Group(groupName).waitForUser2();

                string connectedUser = (Context.User.Identity.Name).ToString();

                //ef notandi sem tengist er ekki user 1
                if (connectedUser != user1)
                {
                    var currentGame = GetCurrentgame(nGameId);

                    if (currentGame.user2 == null)
                    {
                        currentGame.user2 = Context.User.Identity.Name;

                        GetCurrentgame(nGameId).NumberOfPlayers = 2;

                        Clients.Group(groupName).showUserTwo(currentGame.user2);

                        //ath initoponent ætti að vera initGameStart
                        currentGame.initOponents = true;

                        context.SaveChanges();
                    }

                    //fall hér sem randomizar hver á að byrja
                    GetWhoStars(nGameId, groupName);

                    context.SaveChanges();

                }

                //initialise counter
                var gameInstance = from i in context.ActiveGames where i.id == nGameId select i;
                foreach (var i in gameInstance)
                {
                    i.counter = 1;
                }

                context.SaveChanges();

            }
        }

        /// <summary>
        /// Client calls this function when an action card is clicked
        /// </summary>
        public void ClickedAction(string userName, string gameId, string cardId)
        {
            int nGameId = 0;
            int nCardId = 0;

            if (Int32.TryParse(cardId, out nCardId) && Int32.TryParse(gameId, out nGameId))
            {
                var currentGame = GetCurrentgame(nGameId);

                if (currentGame.initOponents == true)
                {
                    string currentPlayer = GetCurrentPLayer(nGameId);
                    var GetAct = GetCurrentgame(nGameId);
                    
                    if ((userName == currentPlayer) && (GetAct.usedAction == true))
                    {
                        var action = GetCurrentAction(nGameId, nCardId);

                        if (action == "2")
                        {
                            Add2PointsToCorrentPlayer(nGameId, nCardId, currentPlayer);

                            //vantar rétta id fyrir action card til að geta hidað það
                            Clients.Group(gameId).HideActionCard(cardId);

                            var user1Score = GetUserOneScore(nGameId);
                            var user2Score = GetUserTwoScore(nGameId);

                            Clients.Group(gameId).ShowUpdateScore(user1Score, user2Score);

                            SetActToUsed(nGameId, nCardId);
                        }

                        if (action == "-2")
                        {
                            Take2PointsFromOppisidePlayer(nGameId, nCardId, currentPlayer);

                            //vantar rétta id fyrir action card til að geta hidað það
                            Clients.Group(gameId).hideActionCard(cardId);

                            var user1Score = GetUserOneScore(nGameId);
                            var user2Score = GetUserTwoScore(nGameId);

                            Clients.Group(gameId).ShowUpdateScore(user1Score, user2Score);

                            SetActToUsed(nGameId, nCardId);

                        }

                        if (action == "takeNuberTwo")
                        {
                            //SwitchCardId(nCardId);

                            //finna idImage töluna á frekmsta spili
                            //finna idImage töluna á næst fremsta spili
                            //segja að idImage fyrri = temp
                            //idImage fyrri = idImageseinni
                            //idImage seinni = ifImage fyrri

                            var nobelCard = GetNobelCard(nGameId, nCardId);
                            nobelCard.idImage = 2;
                           

                            //vantar rétta id fyrir action card til að geta hidað það
                            Clients.Group(gameId).hideActionCard(cardId);

                            var user1Score = GetUserOneScore(nGameId);
                            var user2Score = GetUserTwoScore(nGameId);

                            Clients.Group(gameId).ShowUpdateScore(user1Score, user2Score);

                            SetActToUsed(nGameId, nCardId);

                        }
                        
                        GetAct.usedAction = false;

                        context.SaveChanges();
                    }
                }
            }
        }

        /// <summary>
        /// Client calls this function when an nobel card is clicked
        /// </summary>
        public void ClickedNobel(string userName, string gameId, string cardId)
        {
            int nGameId = 0;
            int nCardId = 0;

            if (Int32.TryParse(cardId, out nCardId) && Int32.TryParse(gameId, out nGameId))
            {
                var currentGame = GetCurrentgame(nGameId);

                if (currentGame.initOponents == true)
                {
                    string currentPlayer = GetCurrentPLayer(nGameId);

                    if (userName == currentPlayer)
                    {
                        if (nCardId == currentGame.counter)
                        {
                            Clients.Group(gameId).hideCard(cardId);
                            currentGame.counter += 1;
                            UpdateScore(currentPlayer, nGameId, nCardId);
                            SetNobToUp(nGameId, nCardId);
                            var getAct = GetCurrentgame(nGameId);
                            getAct.usedAction = true;

                            context.SaveChanges();

                            var user1Score = GetUserOneScore(nGameId);
                            var user2Score = GetUserTwoScore(nGameId);

                            Clients.Group(gameId).ShowUpdateScore(user1Score, user2Score);

                            IsGameFinished(nGameId, nCardId, gameId);

                            var user1 = GetUserOne(nGameId);

                            if (currentPlayer == user1)
                            {
                                SetUserTwoToCurrent(nGameId);
                                Clients.Group(gameId).Player2Turn();
                            }
                            else
                            {
                                SetUserOneToCurrent(nGameId);
                                Clients.Group(gameId).Player1Turn();
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Function that gets user 1 from database
        /// </summary>
        public string GetUserOne(int nGameId)
        {
            var getUser1 = (from p in context.ActiveGames where p.id == nGameId select p.user1).SingleOrDefault();
            return getUser1;
        }

        /// <summary>
        /// Function that gets user 2 from database
        /// </summary>
        public string GetUserTwo(int nGameId)
        {
            var getUser2 = (from p in context.ActiveGames where p.id == nGameId select p.user2).SingleOrDefault();
            return getUser2;
        }

        /// <summary>
        /// Function that gets the player whos turn it is in the game
        /// </summary>
        public string GetCurrentPLayer(int nGameId)
        {
            var getCurrent = ((from p in context.ActiveGames where p.id == nGameId select p.currentPlayer).SingleOrDefault()).ToString();

            return getCurrent;
        }

        /// <summary>
        /// Function that sets the current user to user1
        /// </summary>
        public void SetUserOneToCurrent(int nGameId)
        {
            string nameOfUserOne = ((from p in context.ActiveGames where p.id == nGameId select p.user1).SingleOrDefault()).ToString();
            var currentGameObject = (from p in context.ActiveGames where p.id == nGameId select p).SingleOrDefault();

            currentGameObject.currentPlayer = nameOfUserOne;

            context.SaveChanges();
        }

        /// <summary>
        /// Function that sets the current user to user2
        /// </summary>
        public void SetUserTwoToCurrent(int nGameId)
        {
            string nameOfUserTwo = ((from p in context.ActiveGames where p.id == nGameId select p.user2).SingleOrDefault()).ToString();
            var currentGameObject = (from p in context.ActiveGames where p.id == nGameId select p).SingleOrDefault();

            currentGameObject.currentPlayer = nameOfUserTwo;

            context.SaveChanges();
        }

        /// <summary>
        /// The function retreives the correct nobel card from database
        /// </summary>
        public GuiNobImgInfo GetNobelCard(int nGameId, int nCardId)
        {
            var gameInstanceNobels = from i in context.GuiNobImgInfo where i.idGameInstance == nGameId select i;
            var nobelCard = (from i in gameInstanceNobels where i.idImage == nCardId select i).SingleOrDefault();

            return nobelCard;
        }

        /// <summary>
        /// Gets the current game instance from database
        /// </summary>
        public ActiveGames GetCurrentgame(int nGameId)
        {
            var getCurrent = (from p in context.ActiveGames where p.id == nGameId select p).SingleOrDefault();

            return getCurrent;
        }

        /// <summary>
        /// Function that adds the amount of score that the nobel has to user 1 score
        /// </summary>
        public void SetUserOneScore(int nGameId, int nCardId, string userName)
        {
            var gameInstance = GetCurrentgame(nGameId);
            var nobelCard = GetNobelCard(nGameId, nCardId);
            var aUser = (from p in context.UserModel where p.userName == userName select p).SingleOrDefault();

            gameInstance.userOneScore += nobelCard.nobelObject.value;
            aUser.scoreGui += nobelCard.nobelObject.value;

            context.SaveChanges();
        }

        /// <summary>
        /// Function that adds the amount of score that the nobel has to user 2 score
        /// </summary>
        public void SetUserTwoScore(int nGameId, int nCardId, string userName)
        {
            var gameInstance = GetCurrentgame(nGameId);
            var nobelCard = GetNobelCard(nGameId, nCardId);
            var aUser = (from p in context.UserModel where p.userName == userName select p).SingleOrDefault();

            gameInstance.userTwoScore += nobelCard.nobelObject.value;
            aUser.scoreGui += nobelCard.nobelObject.value;

            context.SaveChanges();
        }

        /// <summary>
        /// Function that sets the nobel that was clicked to "up" in database
        /// </summary>
        public void SetNobToUp(int nGameId, int nCardId)
        {
            var nobelCard = GetNobelCard(nGameId, nCardId);

            nobelCard.userGotNob = "up";

            context.SaveChanges();
        }

        /// <summary>
        /// The function updates the score of the player who just clicked a card
        /// </summary>
        public void UpdateScore(string userName, int nGameId, int nCardId)
        {
            var userOne = GetUserOne(nGameId);

            if (userOne == userName)
            {
                SetUserOneScore(nGameId, nCardId, userName);
            }
            else
            {
                SetUserTwoScore(nGameId, nCardId, userName);
            }
        }

        /// <summary>
        /// Gets the current score of user1 from database
        /// </summary>
        public int GetUserOneScore(int nGameId)
        {
            var getScore = (from p in context.ActiveGames where p.id == nGameId select p.userOneScore).SingleOrDefault();

            return getScore;
        }

        /// <summary>
        /// Gets the current score of user2 from database
        /// </summary>
        public int GetUserTwoScore(int nGameId)
        {
            var getScore = (from p in context.ActiveGames where p.id == nGameId select p.userTwoScore).SingleOrDefault();

            return getScore;
        }

        /// <summary>
        /// Fucntion that checks if game is over
        /// </summary>
        public void IsGameFinished(int nGameId, int nCardId, string groupName)
        {
            var finished = (from p in context.GuiNobImgInfo where (p.idGameInstance == nGameId) && (p.userGotNob == "up") select p).ToList();

            if (finished.Count() == 6)
            {
                var winner = GetWinner(nGameId);
                var scoreOfWinner = GetWinnerScore(nGameId);

                Clients.Group(groupName).GameFinished(nCardId, winner, scoreOfWinner);

                SetGameToFinished(nGameId);

                context.SaveChanges();
            }
        }

        /// <summary>
        /// Gets the winning score
        /// </summary>
        public int GetWinnerScore(int nGameId)
        {
            var user1 = GetUserOne(nGameId);
            var user2 = GetUserTwo(nGameId);
            var user1Score = GetUserOneScore(nGameId);
            var user2Score = GetUserTwoScore(nGameId);

            if (user1Score > user2Score)
            {
                return user1Score;
            }
            else if (user1Score < user2Score)
            {
                return user2Score;
            }
            else
            {
                return user1Score; 
            }
        }

        /// <summary>
        /// Randomize what player starts and set him to currentPlayer
        /// </summary>
        public void GetWhoStars(int nGameId, string groupName)
        {
            List<int> whoStarts = new List<int>();
            whoStarts.Add(1); //user1
            whoStarts.Add(2); //user2
            var GetAct = GetCurrentgame(nGameId);
            GetAct.usedAction = true;

            context.SaveChanges();

            whoStarts = whoStarts.OrderBy(emp => Guid.NewGuid()).ToList();

            if (whoStarts[0] == 1) // if this is true, user1 starts the game
            {
                //sets currentPlayer as user1
                SetUserOneToCurrent(nGameId);
                Clients.Group(groupName).Player1Starts();
            }
            else //if this is true, user2 starts the game
            {
                //sets currentPlayer as user2
                SetUserTwoToCurrent(nGameId);
                Clients.Group(groupName).Player2Starts();
            }
        }

        /// <summary>
        /// Change game from finished=false to finished=true in database
        /// </summary>
        public void SetGameToFinished(int nGameId)
        {
            var finishedGame = (from p in context.ActiveGames where p.id == nGameId select p).SingleOrDefault();
            finishedGame.finished = true;

            context.SaveChanges();
        }

        /// <summary>
        /// Gets the name of the winner
        /// </summary>
        public string GetWinner(int nGameId)
        {
            var user1 = GetUserOne(nGameId);
            var user2 = GetUserTwo(nGameId);
            var user1Score = GetUserOneScore(nGameId);
            var user2Score = GetUserTwoScore(nGameId);

            if (user1Score > user2Score)
            {
                return user1;
            }
            else if (user1Score < user2Score)
            {
                return user2;
            }
            else
            {
                //if there is a tie
                return (user1 + user2);
            }
        }

        //----------Below are functions for Action Cards -----------------------------

        /// <summary>
        /// Gets the value of the current Action card from database
        /// </summary>
        public string GetCurrentAction(int nGameId, int nCardId)
        {
            var gameInstanceAction = from i in context.GuiActImgInfo where i.idGameInstance == nGameId select i;
            var actionCard = (from i in gameInstanceAction where i.idImage == nCardId select i).SingleOrDefault();

            var theAction = actionCard.actionObject.value;

            return theAction;

        }

        /// <summary>
        /// Gets the current action card object
        /// </summary>
        public GuiActImgInfo GetActCard(int nGameId, int nCardId)
        {
            var gameInstanceAction = from i in context.GuiActImgInfo where i.idGameInstance == nGameId select i;
            var actionCard = (from i in gameInstanceAction where i.idImage == nCardId select i).SingleOrDefault();

            return actionCard;
        }

        /// <summary>
        /// Adds two points to the player who's turn it is
        /// </summary>
        public void Add2PointsToCorrentPlayer(int nGameId, int nCardId, string userName)
        {
            var userOne = GetUserOne(nGameId);

            if (userOne == userName)
            {
                SetUserOneScore2Pts(nGameId, nCardId, userName);
            }
            else
            {
                SetUserTwoScore2Pts(nGameId, nCardId, userName);
            }
        }

        /// <summary>
        /// withdraw two points from the oponent
        /// </summary>
        public void Take2PointsFromOppisidePlayer(int nGameId, int nCardId, string userName)
        {
            var userOne = GetUserOne(nGameId);

            if (userOne == userName)
            {
                Take2PtsFromUserTwo(nGameId, nCardId, userName);
            }
            else
            {
                Take2PtsFromUserOne(nGameId, nCardId, userName);
            }
        }

        /// <summary>
        /// Add two points to user1
        /// </summary>
        public void SetUserOneScore2Pts(int nGameId, int nCardId, string userName)
        {
            var gameInstance = GetCurrentgame(nGameId);
            var aUser = (from p in context.UserModel where p.userName == userName select p).SingleOrDefault();

            gameInstance.userOneScore += 2;
            aUser.scoreGui += 2;

            context.SaveChanges();
        }

        /// <summary>
        /// Add two points to user2 
        /// </summary>
        public void SetUserTwoScore2Pts(int nGameId, int nCardId, string userName)
        {
            var gameInstance = GetCurrentgame(nGameId);
            var aUser = (from p in context.UserModel where p.userName == userName select p).SingleOrDefault();


            gameInstance.userTwoScore += 2;
            aUser.scoreGui += 2;

            context.SaveChanges();
        }

        /// <summary>
        /// withdraw two points from user1
        /// </summary>
        public void Take2PtsFromUserOne(int nGameId, int nCardId, string userName)
        {
            var gameInstance = GetCurrentgame(nGameId);
            var aUser = (from p in context.UserModel where p.userName == userName select p).SingleOrDefault();

            gameInstance.userOneScore -= 2;
            aUser.scoreGui -= 2;

            context.SaveChanges();
        }

        /// <summary>
        /// withdraw two points from user2
        /// </summary>
        public void Take2PtsFromUserTwo(int nGameId, int nCardId, string userName)
        {
            var gameInstance = GetCurrentgame(nGameId);
            var aUser = (from p in context.UserModel where p.userName == userName select p).SingleOrDefault();


            gameInstance.userTwoScore -= 2;
            aUser.scoreGui -= 2;

            context.SaveChanges();
        }

        /// <summary>
        /// change status of action card to "used" in database
        /// </summary>
        public void SetActToUsed(int nGameId, int nCardId)
        {
            var actCard = GetActCard(nGameId, nCardId);

            actCard.userGotAction = "used";

            context.SaveChanges();
        }
    }
}